# SERVER MAIL
# --------------------------------------------------------------------------
# Sends and manages emails on the app.

class Mail

    fs = require "fs"
    logger = require "./logger.coffee"
    mailer = require "nodemailer"
    settings = require "./settings.coffee"

    # SMTP object will be instantiated on `init`.
    smtp = null

    # Templates cache to avoid disk reads.
    templateCache = {}


    # INIT
    # --------------------------------------------------------------------------

    # Init the SMTP transport, but only if `smtpHost` and `smtpPort` are defined on the [settings](settings.html).
    init: =>
        if settings.Email.smtpHost? and settings.Email.smtpHost isnt "" and settings.Email.smtpPort > 0
            smtp = mailer.createTransport "SMTP",
                debug: settings.General.debug,
                host: settings.Email.smtpHost,
                port: settings.Email.smtpPort,
                secureConnection: settings.Email.smtpSecure,
                auth:
                    user: settings.Email.smtpUser,
                    pass: settings.Email.smtpPassword
            if settings.General.debug
                logger.info "Mail.init", settings.Email.smtpHost, settings.Email.smtpPort
        else if settings.General.debug
            logger.info "Mail.init", "No SMTP host/port specified.", "No emails will be sent out!"


    # OUTBOUND
    # --------------------------------------------------------------------------

    # Sends an email to the specified address. The `obj` will be parsed and transformed
    # to a HTML formatted message.
    send: (obj, subject, toAddress, fromAddress) ->
        if not smtp?
            if settings.General.debug
                logger.warn "Mail.send", "SMTP transport wasn't initiated. Abort!", subject, "to #{toAddress}"
            return

        if not obj?
            if settings.General.debug
                logger.warn "Mail.send", "Parameter obj is null. Abort!", subject, "to #{toAddress}"
            return

        # Set from to default address if no `fromAddress` was set and create the options object.
        fromAddress = "#{settings.General.appTitle} <#{settings.Email.from}>" if not fromAddress?
        options = {}

        if toAddress.indexOf("<") < 3
            toName = toAddress
        else
            toName = toAddress.substring 0, toAddress.indexOf("<") - 1

        # Check if `obj` is a countdown and generate the HTML based on its details.
        if obj.attributes? and obj.attributes.friendlyUrl? and obj.attributes.targetDate?
            html = @getTemplate "countdown"
            html = @parseTemplate html, obj.attributes
        # Otherwise assume `obj` is the message itself.
        else
            html = obj.toString()

        # Replace common keywords.
        html = @parseTemplate html, {to: toName}

        # Set the message options.
        options.from = fromAddress
        options.to = toAddress
        options.subject = subject
        options.html = html

        # Send the email.
        smtp.sendMail options, (err, result) ->
            if err?
                logger.error "Mail.send", "Could not send message: #{subject} to #{toAddress}", err
            else if settings.General.debug
                logger.info "Mail.send", subject, "to #{toAddress}", "from #{fromAddress}"


    # TEMPLATES
    # --------------------------------------------------------------------------

    # Load the specified template from the cache or from the disk if it wasn't loaded yet.
    # Templates are stored inside the `/server/emailtemplates` folder by default,
    # and should have a .html extension.
    getTemplate: (name) =>
        if templateCache[name]?
            return templateCache[name]

        # Read base and `name` template and merge them together.
        base = fs.readFileSync "#{settings.Path.emailTemplatesDir}base.html"
        template = fs.readFileSync "#{settings.Path.emailTemplatesDir}#{name}.html"
        result = base.toString().replace "{contents}", template.toString()

        # Save to cache.
        templateCache[name] = result

        return result

    # Parse the specified template and replace `keywords` with the correspondent values of
    # the `obj` argument. For example if keywords is `{id: 1, friendlyUrl: "abc"}` then the tags
    # `{id}` and `{friendlyUrl}` will be replaced with the values 1 and abc.
    parseTemplate: (template, keywords) =>
        template = template.toString()

        for key, value of keywords
            template = template.replace new RegExp("\\{#{key}\\}", "gi"), value

        return template


# Singleton implementation
# --------------------------------------------------------------------------
Mail.getInstance = ->
    @instance = new Mail() if not @instance?
    return @instance

module.exports = exports = Mail.getInstance()